-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change DEFINE-CONDITION: signals a TYPE-ERROR if trying to supertype a non-CONDITION #11
base: master
Are you sure you want to change the base?
Conversation
DEFINE-CONDITION-INTERNAL to handle internal conditions (DEFINE-CONDITION-INTERNAL is necessary because we don't know beforehand the relationship between the internal conditions and CONDITION, needed by COERCE-CONDITION-SUPERTYPES.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the patch! I've left two general remarks, plus a request for an addition to the test suite that verifies those scenarios.
src/condition-hierarchy.lisp
Outdated
@@ -8,13 +8,13 @@ | |||
;;; documentation strings due to the repetitiveness of the contents of this | |||
;;; file. | |||
|
|||
(define-condition warning (condition) ()) | |||
(define-condition-internal warning (condition) ()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these changes from define-condition
to define-condition-internal
should be required. Since condition
already names a condition type, we should be able to use define-condition
instead of define-condition-internal
.
Why were they added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When running the test system with DEFINE-CONDITION here, I get INVALID INITIALIZATION ARGUMENTS errors like:
;; Invalid initialization arguments:
;; :EXPECTED-TYPE, :DATUM
;; in call for class #<STANDARD-CLASS PORTABLE-CONDITION-SYSTEM::CASE-FAILURE>.
;; [Condition of type SB-PCL::INITARG-ERROR]
;; See also:
;; Common Lisp Hyperspec, 7.1.2 [:section]
The way COERCE-CONDITION-SUPERTYPES was defined, it was filtering out PCS:SERIOUS-CONDITION and PCS:ERROR from the PCS::CASE-FAILURE condition, for example. But they were superclasses of PCS::CASE-FAILURE before the changes. I assume SUBTYPEP in COERCE-CONDITION-SUPERTYES couldn't tell the relations at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is what I feared - that subtypep
, when run at compile-time, does not have information about class definitions. If left as-is, this is going to propagate into users' code, and this may break in weird ways for them where loading a file is going to have different semantics than compiling it.
A possible (and better!) solution I can imagine is adding a check into the class (re)definition process itself - e.g. a :before
method on ensure-class-using-class
can ensure that all superclasses are condition
or proper subclasses of condition
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this isn't acceptable. Thank you for the feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm following your suggestion about ensure-class-using-class
, and came across a doubt. What should be the behavior of:
(define-condition foo-condition () ())
(defclass bar (foo-condition) ())
?
sbcl, for example, signals an INVALID-SUPERCLASS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can either leave this behavior undefined (because people are supposed to unconditionally use define-condition
to define condition types), or signal our own error in such a situation.
…a non-CONDITION Other changes: Adds a INVALID-SUPERCLASS condition. Signals INVALID-SUPERCLASS when a DEFCLASS outside of DEFINE-CONDITION tries to superclass a CONDITION.
…a non-CONDITION Merge branch 'issue-define-condition' of github.com:lycankrammer/portable-condition-system into issue-define-condition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about the long wait - I did not notice that you have made changes after my initial round of reviews and that you requested another review.
@@ -47,17 +47,44 @@ PRINT-OBJECT method defined on the class named by NAME." | |||
`(let ((,method (find-method #'print-object '() '(,name t) nil))) | |||
(when ,method (remove-method #'print-object ,method))))) | |||
|
|||
(defvar *in-define-condition-p* nil | |||
"This will be dynamically binded to indicate an entry into DEFINE-CONDITION, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/binded/bound/
(defmethod ensure-class-using-class :before (class name &rest args &key direct-superclasses &allow-other-keys) | ||
"In DEFINE-CONDITION: Signals a TYPE-ERROR at the first direct superclass unconforming | ||
with DEFINE-CONDITION, that is, the first encountered superclass in DIRECT-SUPERCLASSES | ||
that is not a subtype of CONDITION. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...subclass of CONDITION
:do (when (not (subtypep superclass 'condition)) | ||
(error 'type-error | ||
:datum superclass | ||
:expected-type 'condition))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error is wrong - such a TYPE-ERROR would mean that the superclass is not of type CONDITION, whereas here the error should say that the superclass is not a subtype of CONDITION. Hence, I think that you should go for signaling INVALID-SUPERCLASS in here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
encountered superclass that is subtype of CONDITION." | ||
(declare (ignore args)) | ||
(if *in-define-condition-p* | ||
(loop :for superclass :in direct-superclasses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe switch out the LOOPs for DOLISTs? Just a style thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
(error 'type-error | ||
:datum superclass | ||
:expected-type 'condition))) | ||
(loop :for superclass :in direct-superclasses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
(invalid-superclass-superclass condition) | ||
(invalid-superclass-reason condition))) | ||
|
||
(define-condition invalid-superclass (condition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an obvious ERROR rather than a CONDITION.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
(if *in-define-condition-p* | ||
(loop :for superclass :in direct-superclasses | ||
:do (when (not (subtypep superclass 'condition)) | ||
(error 'type-error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am kind of interested in the topic of dogfooding ourselves with this one. We will be using PCS:ERROR
to signal PCS:INVALID-SUPERCLASS
during class definition, which will mostly only work with Slime if PCS integration is loaded. Nice.
`(,(expand-define-condition-remove-report-method name))) | ||
',name))) | ||
`(progn | ||
(let ((*in-define-condition-p* t)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This solution creates some new problems. The main issue of having a LET
here is that the DEFCLASS
is no longer toplevel which means that condition classes are not going to be picked up by the compiler, which can result in undefined type warnings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure its a good idea, but an alternative I see would be to add a dummy class to supertypes
here in EXPAND-DEFINE-CONDITION
. Do you have any suggestion?
@@ -579,6 +579,41 @@ | |||
(notnot-mv (typep #'condition-27/s1 'generic-function)) | |||
t) | |||
|
|||
;;; Test non-CONDITION supertypes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this whole section to more-tests.lisp
? I'd rather not modify the ANSI-TEST files themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
DEFINE-CONDITION seems to have an issue. When called with an object that is not a
CONDITION's subtype, it accepts it anyway:
Adds COERCE-CONDITION-SUPERTYPES and changes EXPAND-DEFINE-CONDITION.
Add DEFINE-CONDITION-INTERNAL to handle internal conditions (DEFINE-CONDITION-INTERNAL
is necessary because we don't know beforehand the relationship between the
internal conditions and CONDITION, needed by COERCE-CONDITION-SUPERTYPES.)
The proposed fix results in:
I'm using SBCL 2.0.11